package connect4.swing2;

/**
 * File: GUI.java
 * Author: Brian Borowski
 * Date created: August 27, 2012
 * Date last modified: September 3, 2012
 */
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.border.BevelBorder;

public class GUI extends JFrame {
	private static final long serialVersionUID = 1L;

	private final ApplicationStarter applicationStarter;
	private final ConnectFourConfig config;
	private final GamePanel gamePanel;
	private final JLabel statusLabel, gameTypeDifficultyLabel;
	private JRadioButtonMenuItem humanAdrianAIItem, adrianAIHumanItem,
			humanYiuAIItem, yiuAIHumanItem, yiuAIAdrianAIItem,
			adrianAIYiuAIItem, twoPlayerItem, beginnerItem, intermediateItem,
			advancedItem, expertItem, superExpertItem, selectedGameType,
			selectedDifficultyLevel;
	private JMenu difficultyLevelMenu;

	public GUI(final ApplicationStarter appStarter) {
		super(Application.NAME);
		this.applicationStarter = appStarter;
		config = new ConnectFourConfig(ConnectFourConfig.HUMAN_ADRIANAI,
				ConnectFourConfig.EXPERT);

		setJMenuBar(getCreatedMenuBar());

		statusLabel = new JLabel("Welcome to " + Application.NAME + ".");
		gameTypeDifficultyLabel = new JLabel(getGameTypeDifficultyStatus());
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.insets = new Insets(0, 2, 0, 2);
		gbc.weightx = 1;
		gbc.anchor = GridBagConstraints.LINE_START;
		final JPanel statusPanel1 = new JPanel();
		statusPanel1.setLayout(new GridBagLayout());
		statusPanel1.setBorder(new BevelBorder(BevelBorder.LOWERED));
		statusPanel1.add(statusLabel, gbc);
		statusPanel1.setPreferredSize(new Dimension(250, 25));
		final JPanel statusPanel2 = new JPanel();
		statusPanel2.setLayout(new GridBagLayout());
		statusPanel2.setBorder(new BevelBorder(BevelBorder.LOWERED));
		statusPanel2.add(gameTypeDifficultyLabel, gbc);
		statusPanel2.setPreferredSize(new Dimension(190, 25));

		final JPanel statusPanel = new JPanel();
		statusPanel.setLayout(new GridBagLayout());
		gbc.insets = new Insets(1, 0, 0, 0);
		gbc.weightx = 0.7;
		gbc.weighty = 0;
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridwidth = 1;
		gbc.gridwidth = GridBagConstraints.RELATIVE;
		gbc.fill = GridBagConstraints.BOTH;
		statusPanel.add(statusPanel1, gbc);
		gbc.weightx = 0.3;
		gbc.gridx = 1;
		gbc.gridwidth = GridBagConstraints.REMAINDER;
		statusPanel.add(statusPanel2, gbc);

		gamePanel = new GamePanel(config, statusLabel);

		final Container contentPane = getContentPane();
		contentPane.add(gamePanel, BorderLayout.CENTER);
		contentPane.add(statusPanel, BorderLayout.PAGE_END);

		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		addWindowListener(new ClosingWindowListener(gamePanel));

		setResizable(false);
		pack();
		setLocationRelativeTo(null);
		setVisible(true);
		setFocusable(true);
		requestFocus();
		gamePanel.requestFocusInWindow(); // Must request focus to receive key
											// events.
	}

	private String getGameTypeDifficultyStatus() {
		StringBuilder builder = new StringBuilder();
		int gameType = config.getGameType();
		if (gameType == ConnectFourConfig.HUMAN_ADRIANAI) {
			builder.append("Human vs. Adrian AI");
		} else if (gameType == ConnectFourConfig.ADRIANAI_HUMAN) {
			builder.append("Adrian AI vs. Human");
		} else if (gameType == ConnectFourConfig.HUMAN_YIUAI) {
			builder.append("Yiu AI vs. Human");
		} else if (gameType == ConnectFourConfig.YIUAI_HUMAN) {
			builder.append("Human vs. Yiu AI");
		} else if (gameType == ConnectFourConfig.ADRIANAI_YIUAI) {
			builder.append("Adrian AI vs. Yiu AI");
		} else if (gameType == ConnectFourConfig.YIUAI_ADRIANAI) {
			builder.append("Yiu AI vs. Adrian AI");
		} else {
			builder.append("Two player");
		}
		if (gameType != ConnectFourConfig.HUMAN_HUMAN) {
			int difficultyLevel = config.getDifficultyLevel();
			switch (difficultyLevel) {
			case ConnectFourConfig.BEGINNER:
				builder.append(", Beginner");
				break;
			case ConnectFourConfig.INTERMEDIATE:
				builder.append(", Intermediate");
				break;
			case ConnectFourConfig.ADVANCED:
				builder.append(", Advanced");
				break;
			case ConnectFourConfig.EXPERT:
				builder.append(", Expert");
				break;
			default:
				builder.append(", Super Expert");
				break;
			}
		}
		return builder.toString();
	}

	private JMenu createGameTypeMenu() {
		JMenu gameTypeMenu = new JMenu("Game Type");
		gameTypeMenu.setMnemonic('G');
		humanAdrianAIItem = new JRadioButtonMenuItem("Human vs. Adrian AI",
				true);
		humanAdrianAIItem.setMnemonic('H');
		humanAdrianAIItem.addActionListener(new GameTypeActionListener(this));
		selectedGameType = humanAdrianAIItem;

		adrianAIHumanItem = new JRadioButtonMenuItem("Adrian AI vs. Human",
				false);
		adrianAIHumanItem.setMnemonic('A');
		adrianAIHumanItem.addActionListener(new GameTypeActionListener(this));

		humanYiuAIItem = new JRadioButtonMenuItem("Human vs. Yiu AI", false);
		humanYiuAIItem.setMnemonic('m');
		humanYiuAIItem.addActionListener(new GameTypeActionListener(this));

		yiuAIHumanItem = new JRadioButtonMenuItem("Yiu AI vs. Human", false);
		yiuAIHumanItem.setMnemonic('i');
		yiuAIHumanItem.addActionListener(new GameTypeActionListener(this));

		adrianAIYiuAIItem = new JRadioButtonMenuItem("Adrian AI vs. Yiu AI",
				false);
		adrianAIYiuAIItem.setMnemonic('d');
		adrianAIYiuAIItem.addActionListener(new GameTypeActionListener(this));

		yiuAIAdrianAIItem = new JRadioButtonMenuItem("Yiu AI vs. Adrian AI",
				false);
		yiuAIAdrianAIItem.setMnemonic('u');
		yiuAIAdrianAIItem.addActionListener(new GameTypeActionListener(this));

		twoPlayerItem = new JRadioButtonMenuItem("Two Player", false);
		twoPlayerItem.setMnemonic('T');
		twoPlayerItem.addActionListener(new GameTypeActionListener(this));

		gameTypeMenu.add(humanAdrianAIItem);
		gameTypeMenu.add(adrianAIHumanItem);
		gameTypeMenu.add(humanYiuAIItem);
		gameTypeMenu.add(yiuAIHumanItem);
		gameTypeMenu.add(adrianAIYiuAIItem);
		gameTypeMenu.add(yiuAIAdrianAIItem);
		gameTypeMenu.add(twoPlayerItem);

		ButtonGroup gameTypeGroup = new ButtonGroup();
		gameTypeGroup.add(humanAdrianAIItem);
		gameTypeGroup.add(adrianAIHumanItem);
		gameTypeGroup.add(humanYiuAIItem);
		gameTypeGroup.add(yiuAIHumanItem);
		gameTypeGroup.add(adrianAIYiuAIItem);
		gameTypeGroup.add(yiuAIAdrianAIItem);
		gameTypeGroup.add(twoPlayerItem);

		return gameTypeMenu;
	}

	private JMenu createDifficultyMenu() {
		JMenu difficultyLevelMenu = new JMenu("Difficulty Level");
		difficultyLevelMenu.setMnemonic('D');
		beginnerItem = new JRadioButtonMenuItem("Beginner", false);
		beginnerItem.setMnemonic('B');
		beginnerItem.addActionListener(new DifficultyLevelActionListener(this));
		intermediateItem = new JRadioButtonMenuItem("Intermediate", false);
		intermediateItem.setMnemonic('I');
		intermediateItem.addActionListener(new DifficultyLevelActionListener(
				this));
		advancedItem = new JRadioButtonMenuItem("Advanced", false);
		advancedItem.setMnemonic('A');
		advancedItem.addActionListener(new DifficultyLevelActionListener(this));
		expertItem = new JRadioButtonMenuItem("Expert", true);
		expertItem.setMnemonic('E');
		expertItem.addActionListener(new DifficultyLevelActionListener(this));
		selectedDifficultyLevel = expertItem;

		superExpertItem = new JRadioButtonMenuItem("Super Expert", false);
		superExpertItem.setMnemonic('S');
		superExpertItem.addActionListener(new DifficultyLevelActionListener(
				this));

		difficultyLevelMenu.add(beginnerItem);
		difficultyLevelMenu.add(intermediateItem);
		difficultyLevelMenu.add(advancedItem);
		difficultyLevelMenu.add(expertItem);
		difficultyLevelMenu.add(superExpertItem);

		ButtonGroup difficultyLevelGroup = new ButtonGroup();
		difficultyLevelGroup.add(beginnerItem);
		difficultyLevelGroup.add(intermediateItem);
		difficultyLevelGroup.add(advancedItem);
		difficultyLevelGroup.add(expertItem);
		difficultyLevelGroup.add(superExpertItem);

		return difficultyLevelMenu;
	}

	private JMenuBar getCreatedMenuBar() {
		final JMenuBar menuBar = new JMenuBar();
		final JMenu fileMenu = new JMenu("File");
		fileMenu.setMnemonic('F');

		KeyStroke ks;
		final JMenuItem newGame = new JMenuItem("New Game");
		ks = KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.ALT_MASK);
		newGame.setAccelerator(ks);
		newGame.setMnemonic(KeyEvent.VK_N);
		newGame.addActionListener(new NewActionListener());
		fileMenu.add(newGame);
		fileMenu.add(new JSeparator());

		final JMenuItem exitItem = new JMenuItem("Exit");
		ks = KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.ALT_MASK);
		exitItem.setAccelerator(ks);
		exitItem.setMnemonic(KeyEvent.VK_X);
		exitItem.addActionListener(new ExitActionListener());
		fileMenu.add(exitItem);

		final JMenu optionsMenu = new JMenu("Options");
		optionsMenu.setMnemonic('O');

		final JMenu gameTypeMenu = createGameTypeMenu();

		difficultyLevelMenu = createDifficultyMenu();

		optionsMenu.add(gameTypeMenu);
		optionsMenu.add(difficultyLevelMenu);

		final JMenuItem aboutItem = new JMenuItem("About");
		aboutItem.setMnemonic('A');
		aboutItem.addActionListener(new AboutActionListener(this));

		final JMenu helpMenu = new JMenu("Help");
		helpMenu.setMnemonic('H');
		helpMenu.add(aboutItem);

		menuBar.add(fileMenu);
		menuBar.add(optionsMenu);
		menuBar.add(helpMenu);

		return menuBar;
	}

	private void doApplicationClosing() {
		if (applicationStarter != null) {
			gamePanel.stopGame();
			applicationStarter.close();
		} else {
			System.exit(0);
		}
	}

	class ClosingWindowListener implements WindowListener {
		private final GamePanel gamePanel;

		public ClosingWindowListener(final GamePanel gamePanel) {
			this.gamePanel = gamePanel;
		}

		public void windowClosing(final WindowEvent e) {
			gamePanel.stopGame();
			doApplicationClosing();
		}

		public void windowActivated(final WindowEvent e) {
		}

		public void windowDeactivated(final WindowEvent e) {
		}

		public void windowDeiconified(final WindowEvent e) {
		}

		public void windowIconified(final WindowEvent e) {
		}

		public void windowClosed(final WindowEvent e) {
		}

		public void windowOpened(final WindowEvent e) {
		}
	}

	class ExitActionListener implements ActionListener {

		public void actionPerformed(final ActionEvent e) {
			doApplicationClosing();
		}
	}

	class NewActionListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			gamePanel.reset();
		}
	}

	class GameTypeActionListener implements ActionListener {
		private JFrame parent;

		public GameTypeActionListener(JFrame parent) {
			this.parent = parent;
		}

		public void actionPerformed(final ActionEvent e) {
			int choice = 0;
			boolean isRunning = gamePanel.isRunning();
			if (isRunning) {
				choice = JOptionPane.showConfirmDialog(parent,
						"Changing the game type level at this time will\n"
								+ "require you to abort the current game.\n"
								+ "Abort game and apply settings now?\n",
						"Question", JOptionPane.YES_NO_OPTION);
				if (choice == JOptionPane.NO_OPTION) {
					selectedGameType.setSelected(true);
					return;
				}
			}
			Object o = e.getSource();
			if (o == twoPlayerItem || o == humanYiuAIItem
					|| o == yiuAIHumanItem) {
				difficultyLevelMenu.setEnabled(false);
				if (o == twoPlayerItem) {
					config.setGameType(ConnectFourConfig.HUMAN_HUMAN);
					selectedGameType = twoPlayerItem;
				} else if (o == humanYiuAIItem) {
					config.setGameType(ConnectFourConfig.HUMAN_YIUAI);
					selectedGameType = humanYiuAIItem;
				} else if (o == yiuAIHumanItem) {
					config.setGameType(ConnectFourConfig.YIUAI_HUMAN);
					selectedGameType = yiuAIHumanItem;
				}
			} else {
				difficultyLevelMenu.setEnabled(true);
				if (o == humanAdrianAIItem) {
					config.setGameType(ConnectFourConfig.HUMAN_ADRIANAI);
					selectedGameType = humanAdrianAIItem;
				} else if (o == adrianAIHumanItem) {
					config.setGameType(ConnectFourConfig.ADRIANAI_HUMAN);
					selectedGameType = adrianAIHumanItem;
				} else if (o == yiuAIAdrianAIItem) {
					config.setGameType(ConnectFourConfig.YIUAI_ADRIANAI);
					selectedGameType = yiuAIAdrianAIItem;
				} else if (o == adrianAIYiuAIItem) {
					config.setGameType(ConnectFourConfig.ADRIANAI_YIUAI);
					selectedGameType = adrianAIYiuAIItem;
				}
			}
			gameTypeDifficultyLabel.setText(getGameTypeDifficultyStatus());
			if (isRunning) {
				gamePanel.reset();
			}
		}
	}

	class DifficultyLevelActionListener implements ActionListener {
		private JFrame parent;

		public DifficultyLevelActionListener(JFrame parent) {
			this.parent = parent;
		}

		public void actionPerformed(final ActionEvent e) {
			int choice = 0;
			boolean isRunning = gamePanel.isRunning();
			if (isRunning) {
				choice = JOptionPane.showConfirmDialog(parent,
						"Changing the difficulty level at this time will\n"
								+ "require you to abort the current game.\n"
								+ "Abort game and apply settings now?\n",
						"Question", JOptionPane.YES_NO_OPTION);
				if (choice == JOptionPane.NO_OPTION) {
					selectedDifficultyLevel.setSelected(true);
					return;
				}
			}
			Object o = e.getSource();
			if (o == beginnerItem) {
				config.setDifficulty(ConnectFourConfig.BEGINNER);
				selectedDifficultyLevel = beginnerItem;
			} else if (o == intermediateItem) {
				config.setDifficulty(ConnectFourConfig.INTERMEDIATE);
				selectedDifficultyLevel = intermediateItem;
			} else if (o == advancedItem) {
				config.setDifficulty(ConnectFourConfig.ADVANCED);
				selectedDifficultyLevel = advancedItem;
			} else if (o == expertItem) {
				config.setDifficulty(ConnectFourConfig.EXPERT);
				selectedDifficultyLevel = expertItem;
			} else if (o == superExpertItem) {
				config.setDifficulty(ConnectFourConfig.SUPER_EXPERT);
				selectedDifficultyLevel = superExpertItem;
			}
			gameTypeDifficultyLabel.setText(getGameTypeDifficultyStatus());
			if (isRunning) {
				gamePanel.reset();
			}
		}
	}

	class AboutActionListener implements ActionListener {
		private final JFrame parent;

		public AboutActionListener(final JFrame parent) {
			this.parent = parent;
		}

		public void actionPerformed(final ActionEvent e) {
			final String[] data = { "Version 1.0.2",
					"\u00a9 2012 Brian S. Borowski", "All Rights Reserved.",
					"Build: September 3, 2012" };
			new AboutDialog(parent, Application.NAME, data, "images/icon.png");
		}
	}
}